package com.tianji.learning.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.validation.constraints.NotNull;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;

@RequiredArgsConstructor
@Component
@Slf4j
public class LearningRecordDelayTaskHandler {

    private final StringRedisTemplate redisTemplate;
    private final String KEY_TEMPLATE = "learning:record:{}";
    private DelayQueue<DelayTask<DelayTaskData>> delayTaskQueue = new DelayQueue<>();
    private final LearningRecordMapper recordMapper;
    private final LearningLessonMapper lessonMapper;
    private boolean flag = true;

    @PostConstruct
    public void init(){
        log.info("Bean初始化阶段执行，开启一个新的线程执行延时检测任务...");
//        new Thread(this::handleDelayTask).start();
        CompletableFuture.runAsync(this::handleDelayTask);
    }

    @PreDestroy
    public void destroy(){
        log.info("Bean销毁阶段执行，关闭线程...");
        flag = false;
    }

    /**
     * 处理延迟任务
     */
    public void handleDelayTask(){
        //获取延迟任务
        while (flag){
            try {
                DelayTask<DelayTaskData> take = delayTaskQueue.take();
                //获取延迟任务的数据
                DelayTaskData data = take.getData();

                //查询redis缓存
                LearningRecord redisRecord = readLearningRecordFromCache(data.getLessonId(), data.getSectionId());

                // 3.1、如果不一致，无需处理，直接跳过
                if (ObjectUtil.isEmpty(redisRecord) || ObjectUtil.notEqual(redisRecord.getMoment(), data.getMoment())) {
                    continue;
                }

                // 3.2、如果一致，证明用户已经离开了，需要将Redis中的进度更新到学习记录表，并更新课表数据
                // 3.2.1、更新学习记录表（主要是更新播放进度 moment）
                redisRecord.setFinished(null);
                recordMapper.updateById(redisRecord);

                // 3.2.2、更新课表数据（更新最近学习小节ID、最近学习时间）
                LearningLesson lesson = new LearningLesson();
                lesson.setId(data.getLessonId());
                lesson.setLatestSectionId(data.getSectionId());
                lesson.setLatestLearnTime(LocalDateTime.now().minusSeconds(20));
                lessonMapper.updateById(lesson);

            } catch (InterruptedException e) {
                log.error("获取延迟任务失败，异常信息：{}", e.getMessage());
            }
        }
    }

    /**
     * 查询缓存中是否有该记录
     *
     * @param lessonId
     * @param sectionId
     * @return
     */
    public LearningRecord readLearningRecordFromCache(@NotNull(message = "课表id不能为空") Long lessonId, @NotNull(message = "节的id不能为空") Long sectionId) {
        //组装大key
        String key = StringUtils.format(KEY_TEMPLATE, lessonId);
        //组转小key
        String sectionIdString = sectionId.toString();

        //查询缓存中是否有该记录
        String learningRecord = (String) redisTemplate.opsForHash().get(key, sectionIdString);
        if (ObjectUtil.isEmpty(learningRecord)) {
            return null;
        }

        LearningRecord oldLearningRecord = JSONUtil.toBean(learningRecord, LearningRecord.class);
        return oldLearningRecord;
    }

    /**
     * 将学习记录写入缓存
     *
     * @param learningRecord
     */
    public void writeLearningRecordToCache(LearningRecord learningRecord) {
        //组装大key
        String key = StringUtils.format(KEY_TEMPLATE, learningRecord.getLessonId());
        //组装小key
        String sectionIdString = learningRecord.getSectionId().toString();
        //将对象转为json字符串
        String jsonStr = JSONUtil.toJsonStr(new RedisRecordData(learningRecord));
        //写入缓存
        redisTemplate.opsForHash().put(key, sectionIdString, jsonStr);
        //设置过期时间：1分钟
        redisTemplate.expire(key, Duration.ofMinutes(1));
    }

    /**
     * 清除redis缓存
     * @param lessonId
     * @param sectionId
     */
    public void clearLearningRecordDelayCheckTaskCache(@NotNull(message = "课表id不能为空") Long lessonId, @NotNull(message = "节的id不能为空") Long sectionId) {
        //组装大key
        String key = StringUtils.format(KEY_TEMPLATE,lessonId);
        //组装小key
        String sectionIdString = sectionId.toString();
        //清除缓存
        redisTemplate.opsForHash().delete(key, sectionIdString);
    }

    /**
     * 如果是非第一次学完，将学习记录暂存至redis，并且提交一个延迟任务
     * @param record
     */
    public void addLearningRecordDelayCheckTask(LearningRecord record) {
        //写入缓存
        writeLearningRecordToCache(record);

        //提交延迟任务
        delayTaskQueue.add(new DelayTask<DelayTaskData>(Duration.ofSeconds(20), new DelayTaskData(record)));
    }

    @Data
    class RedisRecordData {
        /**
         * 学习记录的id
         */
        private Long id;
        /**
         * 视频的当前观看时间点，单位秒
         */
        private Integer moment;
        /**
         * 是否完成学习，默认false
         */
        private Boolean finished;

        public RedisRecordData(LearningRecord learningRecord) {
            BeanUtils.copyProperties(learningRecord, this);
        }
    }

    @Data
    class DelayTaskData {
        private Long lessonId;
        private Long sectionId;
        private Integer moment;

        public DelayTaskData(LearningRecord learningRecord) {
            BeanUtils.copyProperties(learningRecord, this);
        }
    }
}
